<?php
/**
 * Data Tranfer Object
 *
 * @category   Epic
 * @package    Epic_Db
 * @author     Epic Core Team <rukanhitokiri@gmail.com>
 */
class Epic_Db_Model_Object implements ArrayAccess
{  
	/**
     * Object attributes
     * @var array
     */
    protected $_data = array();
    
    /**
     * Name of object id field
     * @var string
     */
    protected $_idFieldName = null;
    
    /**
     * Setter/Getter underscore transformation cache
     * @var array
     */
    protected static $_underscoreCache = array();

    /**
     * Constructor
     *
     * By default is looking for first argument as array and assignes it as object attributes
     */
    public function __construct() {
        $args = func_get_args();
        if (empty($args[0])) {
            $args[0] = array();
        }
        $this->_data = $args[0];
    }
    
    /**
     * set name of object id field
     *
     * @param   string $name
     * @return  Epic_Db_Model_Object
     */
    public function setIdFieldName($name)
    {
        $this->_idFieldName = $name;
        return $this;
    }

    /**
     * Retrieve name of object id field
     *
     * @return  string
     */
    public function getIdFieldName()
    {
        return $this->_idFieldName;
    }
    
    /**
     * Set object id field value
     *
     * @param   int $value
     * @return  Epic_Db_Model_Object
     */
    public function setId($value)
    {
        if ($this->getIdFieldName()) {
            $this->setData($this->getIdFieldName(), $value);
        } else {
            $this->setData('id', $value);
        }
        return $this;
    }
    
    /**
     * Retrieve object id
     *
     * @return int
     */
    public function getId()
    {
        if ($this->getIdFieldName()) {
            return $this->getData($this->getIdFieldName());
        }
        return $this->getData('id');
    }
    
    /**
     * Overwrite data in the object.
     *      
     * $key can be string or array.
     * If $key is string, the attribute value will be overwritten by $value
     *
     * If $key is an array, it will overwrite all the data in the object.
     *
     * @param string|array $key
     * @param null|string $value
     * @return Epic_Db_Model_Object
     */
    public function setData($key, $value=null)
    {
        if(is_array($key)) {
            $this->_data = $key;
        } else {
            $this->_data[$key] = $value;
        }
        return $this;
    }
    
    /**
     * Retrieves data from the object
     *
     * If $key is empty will return all the data as an array
     * Otherwise it will return value of the attribute specified by $key
     *
     * @param string $key
     * @return null|string
     */
    public function getData($key='')
    {
        if (''===$key) {
            return $this->_data;
        }
        return isset($this->_data[$key]) ? $this->_data[$key] : null;
    }
    
    /**
     * Unset data from the object.
     *
     * $key can be a string only. Array will be ignored.
     *
     * @param string $key
     * @return Epic_Db_Model_Object
     */
    public function unsetData($key = null)
    {
    	if (is_null($key)) {
    		$this->_data = array();
    	} else {
    		unset($this->_data[$key]);
    	}
    	return $this;
    }
    
    /**
     * Converts field names for setters and geters
     *
     * $this->setMyField($value) === $this->setData('my_field', $value)
     * Uses cache to eliminate unneccessary preg_replace
     *
     * @param string $name
     * @return string
     */
    protected function _underscore($name)
    {
        if (isset(self::$_underscoreCache[$name])) {
            return self::$_underscoreCache[$name];
        }
        $result = strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $name));
        self::$_underscoreCache[$name] = $result;
        return $result;
    }
    
    /**
     * Khoác ngoài thuộc tính set/get
     *
     * @param   string $method
     * @param   array $args
     * @return  mixed
     */
    public function __call($method, $args)
    {
        switch (substr($method, 0, 3)) {
        	case 'set' :
                $key = $this->_underscore(substr($method,3));
                $result = $this->setData($key, isset($args[0]) ? $args[0] : null);
                return $result;

            case 'get' :
                $key = $this->_underscore(substr($method,3));
                $data = $this->getData($key);
                return $data;
        }
        throw new Epic_Db_Exception("Invalid method ".get_class($this)."::".$method."(".print_r($args,1).")");
    }
    
    /**
     * checks whether the object is empty
     *
     * @return boolean
     */
    public function isEmpty()
    {
    	return (empty($this->_data)) ? true : false;
    }
    
    /**
     * Convert object attributes to XML
     *
     * @param  array $arrAttributes array of required attributes
     * @param string $rootName name of the root element
     * @return string
     */
    public function toXml($rootName = 'item', $addOpenTag = false, $addCdata = true)
    {
    	$xml = '';
    	if ($addOpenTag) {
    		$xml.= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
    	}
    	if (!empty($rootName)) {
    		$xml.= '<'.$rootName.'>'."\n";
    	}
    	$xmlModel = new SimpleXMLElement('<node></node>');
    	$arrData = $this->_data;
    	foreach ($arrData as $fieldName => $fieldValue) {
    		if ($addCdata === true) {
    			$fieldValue = "<![CDATA[$fieldValue]]>";
    		} else {
    			$fieldValue = $xmlModel->xmlentities($fieldValue);
    		}
    		$xml.= "<$fieldName>$fieldValue</$fieldName>"."\n";
    	}
    	if (!empty($rootName)) {
    		$xml.= '</'.$rootName.'>'."\n";
    	}
    	return $xml;
    }
    
    /**
     * Implementation of ArrayAccess::offsetSet()
     *
     * @link http://www.php.net/manual/en/arrayaccess.offsetset.php
     * @param string $offset
     * @param mixed $value
     */
    public function offsetSet($offset, $value)
    {
        $this->_data[$offset] = $value;
    }

    /**
     * Implementation of ArrayAccess::offsetExists()
     *
     * @link http://www.php.net/manual/en/arrayaccess.offsetexists.php
     * @param string $offset
     * @return boolean
     */
    public function offsetExists($offset)
    {
        return isset($this->_data[$offset]);
    }

    /**
     * Implementation of ArrayAccess::offsetUnset()
     *
     * @link http://www.php.net/manual/en/arrayaccess.offsetunset.php
     * @param string $offset
     */
    public function offsetUnset($offset)
    {
        unset($this->_data[$offset]);
    }

    /**
     * Implementation of ArrayAccess::offsetGet()
     *
     * @link http://www.php.net/manual/en/arrayaccess.offsetget.php
     * @param string $offset
     * @return mixed
     */
    public function offsetGet($offset)
    {
        return isset($this->_data[$offset]) ? $this->_data[$offset] : null;
    }                 
}